package libra.filter.custom._1;

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequest.Builder;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Mono;

/**
 * <pre>
 * 自定义Filter过滤器
 * 注意事项：
 * 	1、自定义过滤器工厂必须是Spring组件，即IOC容器中的bean。
 * 	2、自定义过滤器工厂必须继承自抽象类AbstractGatewayFilterFactory。
 * 	   2.1、AbstractGatewayFilterFactory还有其他的抽象子类可以继承使用，本Demo采用顶级父类来实现功能
 * 	3、自定义过滤器工厂命名必须是：xxxxGatewayFilterFactory，即以'GatewayFilterFactory'结尾。
 * 	   使用时，Gateway底层会根据'xxxx' + 'GatewayFilterFactory'，来进行反射查找对应的过滤器工厂。
 * 	4、自定义过滤器工厂类中必须实现一个静态内部类 -> public static class Config{ xxx xxx }
 * 	   Config类的作用是用来接收yml配置文件中的配置信息。
 * 	   4.1、经过测试，该项并非必须项，也可以使用外部定义bean来完成Config。
 * 	5、自定义断言工厂类中必须实现shortcutFieldOrder()方法，来完成yml配置与Config类之间的值绑定。
 * 
 * !!! 
 * 本Demo只演示通过ServerWebExchange操作Request、Response的前置处理(设置头)
 * 不演示重写Response响应数据的后置处理案例，如果希望手动重写Response参见以下案例
 * <img width="auto" height="700" src="../../../../../resources/WebFlux.Fliter后置处理示例.png" alt="Fliter后置处理示例">
 * </pre>
 */
@Component
public class SelfBeforeFilterGatewayFilterFactory extends AbstractGatewayFilterFactory<SelfBeforeFilterGatewayFilterFactory.Config> {
	
	// 构造函数需要将自身的静态内部Config类进行传入
	public SelfBeforeFilterGatewayFilterFactory() {
		super(Config.class);
	}
	
	@Override
	public GatewayFilter apply(Config config) {
		// 过滤器功能实现，向请求中添加请求头信息
		return new GatewayFilter() {
			@Override
			public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
				// 通过ServerWebExchange获取当次的Request请求对象
				ServerHttpRequest req = exchange.getRequest();
				// 通过ServerWebExchange获取当次的Response响应对象
				ServerHttpResponse resp = exchange.getResponse();
				
				// 不能通过req.getHeaders()直接向请求头中添加数据; 但可以通过resp.getHeaders()直接向响应头中添加数据; 
				//req.getHeaders().add("req_key_1", "req_val_1");//会抛出UnsupportedOperationException不支持操作异常，底层是个只读ReadOnly的Request
				resp.getHeaders().add("resp_SelfBeforeFilter_key", "resp_SelfBeforeFilter_val");
				
				// 通过req.mutate()获取请求builder生成器，通过该builder重构一个Request来修改请求中的属性值
				Builder builder = req.mutate();
				ServerHttpRequest newRequest = builder.headers(new Consumer<HttpHeaders>() {
					@Override
					public void accept(HttpHeaders t) {
						t.add(config.reqHeaderK, config.reqHeaderV);
					}
				}).build();
				
				// GatewayFilterChain.filter()放行请求至下一个'过滤器'or'实际处理逻辑'
				return chain.filter(exchange.mutate().request(newRequest).build());
			}
		};
	}
	
	/**
	 * 通过该方法向Config类进行“值”设置
	 * 传入的值即为Config中的属性名，通过查找Config中的set方法进行设置“值”
	 */
	@Override
	public List<String> shortcutFieldOrder() {
		return Arrays.asList("reqHeaderK","reqHeaderV");
	}
	
	/**
	 * 内部静态Config配置类，用于存储yml中的配置值
	 * @author libra
	 */
	public static class Config{
		private String reqHeaderK;
		private String reqHeaderV;
		public String getReqHeaderK() {
			return reqHeaderK;
		}
		public void setReqHeaderK(String reqHeaderK) {
			this.reqHeaderK = reqHeaderK;
		}
		public String getReqHeaderV() {
			return reqHeaderV;
		}
		public void setReqHeaderV(String reqHeaderV) {
			this.reqHeaderV = reqHeaderV;
		}
	}
}
